package timer

import (
	"leaf/constant"
	"leaf/log"
	"runtime"
	"time"
)

type Timer interface {
	stop()
	callback() (reset bool)
}

// Once
type Once struct {
	*time.Timer
	cb func()
}

func (once *Once) stop() {
	if once.Timer != nil {
		once.Timer.Stop()
		once.Timer = nil
	}

	once.cb = nil
}

func (once *Once) callback() (reset bool) {
	defer func() {
		once.cb = nil
		if r := recover(); r != nil {
			if constant.LenStackBuf > 0 {
				buf := make([]byte, constant.LenStackBuf)
				l := runtime.Stack(buf, false)
				log.Error("%v: %s", r, buf[:l])
			} else {
				log.Error("%v", r)
			}
		}
	}()

	if once.cb != nil {
		once.cb()
	}

	return
}

// Loop
type Loop struct {
	*Once
	interval time.Duration
}

func (loop *Loop) callback() (reset bool) {
	cb := loop.Once.cb
	loop.Once.callback()

	if loop.Once.Timer != nil {
		loop.Once.cb = cb
		loop.Once.Timer.Reset(loop.interval)
	}

	return true
}

// Cron
type Cron struct {
	*Once
	expr *CronExpr
}

func (cron *Cron) callback() (reset bool) {
	cb := cron.Once.cb
	cron.Once.callback()

	now := time.Now()
	nextTime := cron.expr.Next(now)
	if nextTime.IsZero() {
		return
	}

	if cron.Once.Timer != nil {
		cron.Once.cb = cb
		cron.Once.Timer.Reset(nextTime.Sub(now))
		reset = true
	}

	return
}
